﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Tycho.Runtime;
using System.IO;
using NUnit.Framework;

namespace Tycho.UnitTests.Evaluation {
    public class Script {
        public static void Test (string scriptPath) {
            Test (File.ReadAllText (scriptPath + ".results"), File.ReadAllText (scriptPath + ".results"));
        }

        public static void Test (string script, string expectedResult) {
            Test (script, expectedResult, null);
        }

        public static void Test (string script, string expectedResult, Dictionary<string, AnyObject> objects) {
            script = script.Trim ();
            expectedResult = expectedResult.Trim ();

            TopLevel topLevel = new TopLevel ();

            RuntimeModule.Compile = (source, stackFrame) => topLevel.Compile (source, null, stackFrame);

            AddTestObjects (topLevel, objects);
            StringWriter output = new StringWriter ();
            using (new ThreadContext ()) {
                ThreadContext.Current.SetProperty (Symbols.RuntimePrintStream, new NativeObject<TextWriter> (output));

                AnyObject result = topLevel.Evaluate (script);
                output.Write ("> " + result);
            }

            if (expectedResult.EndsWith (Environment.NewLine)) {
                output.WriteLine ();
            }
            Assert.AreEqual (expectedResult, output.ToString ());
        }

        private static void AddTestObjects (TopLevel level, Dictionary<string, AnyObject> objects) {
            if (objects != null) {
                level.AddModule (Namespaces.Root.GetNamespace ("test"), new MockModuleLoader (objects));
            }
        }

        public static AnyObject Evaluate (string source) {
            TopLevel topLevel = new TopLevel ();
            return topLevel.Evaluate (source);
        }

        public static void AssertEvaluationThrows(Type exception, string source) {
            try {
                Evaluate (source);
                Assert.Fail("expected " + exception);
            } catch (Exception e) {
                if (!exception.IsInstanceOfType (e)) {
                    throw;
                }
            }
        }
    }
}